﻿using System;
using System.Collections.Generic;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sern.Abstracts.Common;
using Sern.Abstracts.Splitter;
using Sern.Abstracts.Tracker.DataContracts;
using Sern.Abstracts.Tracker.ServiceContracts;
using Sern.Abstracts.Tracker.ServiceContracts.Fakes;
using Sern.Common;
using Sern.Common.Fakes;
using Sern.ServiceImplementation;
using Sern.ServiceImplementation.Fakes;
using Sern.ServiceImplementation.MapReduce;
using Microsoft.Practices.Unity;
using LocalRegistrar = Sern.ServiceImplementation.MapReduce.LocalRegistrar;

namespace Sern.Test.ServiceImplementation.MapReduce
{
    [TestClass]
    public class MapReduceClass_SplitterCallsFilter
    {
        List<string> splitStrings = new List<string> { };
        private Action<object, object> _filterCallback;
        private AutoResetEvent resetEvent = new AutoResetEvent(false);

        internal class TestSplitter : MapReduceClientEnvelope<ISplitter<SelfHealingKey<string>, string>, SelfHealingKey<string>, string>
        {
            protected override void Handle(SelfHealingKey<string> key, string value, Action<object, object> nextCallback)
            {
                var split = value.Split(new char[] {' '});
                var position = 0;
                foreach(string s in split)
                {
                    var newKey = SelfHealingKey.CreateFrom(key, s, String.Format(@"{0}_{1}", key.Key, position));
                    nextCallback(newKey, 1.ToString());
                    position++;
                }
            }

            protected override void End()
            {                
            }
        }

        [TestInitialize]
        public void TestInit()
        {
            var clientId = Guid.NewGuid();
            var jobId = Guid.NewGuid();
            
            //Signal when all 3 calls to filter has been made, to let the main thread to continue with assertions.
            var pulseFilterCalled = new Subject<Unit>();
            pulseFilterCalled.Buffer(3).Select(_ => Unit.Default)
                .Subscribe(
                    Observer.Create<Unit>(
                        _ => resetEvent.Set()));


            ComponentLocator.Reset();

            IClientInstance splitter = new TestSplitter();
            splitter.Initialize(new ClientInitializationArgument
                {
                    JobId = jobId,
                    ClientId = clientId,
                    HostName = "MyHostName",
                    LogLevel = LogLevel.None,
                    MapReduceHostUri = "http://my.host",
                    TrackerUri = "http://tracker"
                });

            (new LocalRegistrar() as IComponentRegistrar).Register(ComponentLocator.Unity);

            //TODO: We can have the listener factory be the one to check and make sure that the components are passed in the constructor! In this test.
            //TODO: We can then inject the argument through call to Handle!
            IListenerFactory listenerFactory = new StubIListenerFactory
                {
                    CreateUriUriClientTypeObject =
                        (baseUri, endPtUri, clientType, instance) =>
                        new Listener
                            {
                                Open = () => { },
                                Close = () => { },
                                Dispose = () => { }
                            }
                };

            ComponentLocator.Unity.RegisterInstance(listenerFactory);

            Guid filterNextClientId = Guid.NewGuid();

            ComponentLocator.Unity.RegisterInstance<Func<IClient<ITrackerTelemetryReceiver>>>(
                () =>
                new StubIClient<ITrackerTelemetryReceiver>
                    {
                        ProxyGet = () =>
                                   new StubITrackerTelemetryReceiver()
                                       {
                                           NotifyEndGuid = guid => { },
                                           NotifyIsAliveNotice = notice => { },
                                           NotifyProcessingNotice = notice => { },
                                       }
                    });

            ComponentLocator.Unity.RegisterInstance<Func<IClient<ITracker>>>(
                () =>
                new StubIClient<ITracker>
                {
                    ProxyGet = () =>
                               new StubITracker
                               {
                                   FindActiveClientInfoGuidClientTypeGuidGuidStringString =
                                       (guid, type, arg3, arg4, arg5, arg6) =>
                                       new ClientInfo
                                       {
                                           HostUri = "",
                                           Id = filterNextClientId,
                                           JobId = jobId,
                                           KeyShape = typeof(SelfHealingKey<string>).FullName,
                                           ValueShape = typeof(string).FullName
                                       },
                                   SubscribeGuidGuidClientTypeStringStringStringString = 
                                    (guid, guid1, arg3, arg4, arg5, arg6, arg7) =>
                                        new ClientInfo
                                        {
                                            HostUri = "",
                                            Id = filterNextClientId,
                                            JobId = jobId,
                                            KeyShape = typeof(SelfHealingKey<string>).FullName,
                                            ValueShape = typeof(string).FullName
                                        }
                                    

                               }
                });

            ComponentLocator.Unity.RegisterInstance<Func<IClient<ITrackerTelemetryReceiver>>>(
                () =>
                new StubIClient<ITrackerTelemetryReceiver>
                {
                    ProxyGet = () =>
                               new StubITrackerTelemetryReceiver()
                               {
                                  NotifyEndGuid = guid => { },
                                  NotifyIsAliveNotice = notice => { },
                                  NotifyProcessingNotice = notice => { }
                               }
                });

            ComponentLocator.Unity.RegisterInstance<Func<IClient<ITrackerLogging>>>(
                () =>
                new StubIClient<ITrackerLogging>
                    {
                        ProxyGet = () =>
                                   new StubITrackerLogging
                                       {
                                           LogEmissionTrace = trace => { },
                                           LogLogEntry = entry => { }
                                       }
                    }
                );

            _filterCallback = (o, o1) =>
                {
                    pulseFilterCalled.OnNext(Unit.Default);
                    splitStrings.Add((string) o1);
                };
            ComponentLocator.Unity.RegisterInstance("FilterInvoker", _filterCallback);

            //TODO: Make sure to test that the tracker and tracker telemetry is called at appropriate times ie. each client attempts to call tracker before sending the next bit of information.
            //TODO: Make sure to test that the map reduce client honors them.
            //TODO: CHeck to make sure that the MR client registers the Key and Value types correctly.
            //TODO: Test to make sure that next client gets called as appropriate.
            //TODO: Test all the helpers and the map reduce factories.
            //TODO: Make sure it's registering correctly.
        }

        [TestMethod]
        public void MapReduceRealFilterCalled()
        {
            var realProxy = ComponentLocator.Unity.Resolve<ISplitter<SelfHealingKey<string>, string>>();
            
            realProxy.Handle(new SelfHealingKey<string>
                {
                    Iteration = 0,
                    Key = "0",
                    RedundancyKeys = new Queue<object>(new object[]{"0"})
                },
                "Test test 123");


            var debug = ComponentLocator.Unity.Resolve<Func<IClient<ITrackerLogging>>>();
            
            //TODO: Tracker logging is still not working.
            resetEvent.WaitOne();
            
            Assert.AreEqual(splitStrings[0], "1");
            Assert.AreEqual(splitStrings[1], "1");
            Assert.AreEqual(splitStrings[2], "1");

        }

    }
}
